package com.lauor.smpedr.spring.boot.autoconfigure;

import com.lauor.smpedr.core.SqlGeneratorMysql;
import com.lauor.smpedr.plugin.Interceptor;
import com.lauor.smpedr.session.SqlSessionFactory;
import com.lauor.smpedr.session.provider.DefaultSqlSessionFactory;
import com.lauor.smpedr.spring.session.SpringManagedSqlSessionFactory;
import com.lauor.smpedr.spring.transaction.SpringManagedTransactionFactory;
import com.lauor.smpedr.utils.Str;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.util.ClassUtils;

import javax.sql.DataSource;
import java.lang.reflect.Constructor;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;

/**
 *持久化框架 edr自动配置
 * @author tfq qq:1394965066
 */
@Configuration
@ConditionalOnMissingBean(SqlSessionFactory.class)
@ConditionalOnClass(DefaultSqlSessionFactory.class)
@ConditionalOnBean({DataSource.class, Environment.class})
@AutoConfigureAfter(DataSourceAutoConfiguration.class)//指定顺序
public class SmpEdrAutoConfiguration {

    @Bean
    public SqlSessionFactory buildDefaultSessionFactory(DataSource dataSource, Environment environment, @Autowired(required = false) List<Interceptor> interceptors){
        LoggerFactory.getLogger(SmpEdrAutoConfiguration.class).debug("Init DefaultSqlSessionFactory of SmpEdr");

        String interceptorPaths = environment.getProperty("com.lauor.smpedr.interceptors");
        if (interceptors == null) {
            interceptors = new LinkedList<>();
        }

        com.lauor.smpedr.Configuration configuration = new com.lauor.smpedr.Configuration(dataSource, new SpringManagedTransactionFactory());
        configuration = this.bindInterceptors(interceptorPaths, configuration, interceptors);

        SqlSessionFactory sqlSessionFactory = new SpringManagedSqlSessionFactory(configuration, new SqlGeneratorMysql());
        return sqlSessionFactory;
    }

    //设置拦截器
    private com.lauor.smpedr.Configuration bindInterceptors(String interceptorPaths, com.lauor.smpedr.Configuration configuration, List<Interceptor> interceptors){
        //自动注入的拦截器
        interceptors.forEach(interceptor -> configuration.addInterceptor(interceptor));

        if ( Str.isEmpty(interceptorPaths) ) return configuration;

        String[] interceptorPathArr = interceptorPaths.split(",");
        for (String interceptorPath : interceptorPathArr) {
            try {
                if ( existInterceptor(interceptorPath, interceptors) ){
                    continue;
                }

                Class interceptorCls = ClassUtils.forName(interceptorPath, this.getClass().getClassLoader());
                Constructor constructor = interceptorCls.getDeclaredConstructor();
                configuration.addInterceptor( (Interceptor) constructor.newInstance() );
            } catch (Exception ex){
                LoggerFactory.getLogger(SmpEdrAutoConfiguration.class).error(String.format("error to add interceptor: %s", interceptorPath), ex);
                System.exit(-1);
            }
        }
        return configuration;
    }
    /**
     * @description:拦截器是否已存在
     * @date 17:21 2022/8/5
     * @param clsName
     * @param interceptors
     * @exception
     * @return boolean true/false  存在/不存在
     */
    private boolean existInterceptor(String clsName, List<Interceptor> interceptors){
        for (Interceptor interceptor : interceptors) {
            if ( Objects.equals(clsName, interceptor.getClass().getName()) ){
                return true;
            }
        }
        return false;
    }
}
